package com.regulareport.form.service.impl;

import com.regulareport.common.core.domain.entity.ReportIndexResult;
import com.regulareport.common.core.domain.entity.ReportTask;
import com.regulareport.common.core.domain.entity.SysDept;
import com.regulareport.common.core.domain.model.LoginUser;
import com.regulareport.common.enums.DelFlag;
import com.regulareport.common.enums.FileJsonState;
import com.regulareport.common.utils.SecurityUtils;
import com.regulareport.common.utils.StringUtils;
import com.regulareport.common.utils.poi.ExcelToJsonConverter3;
import com.regulareport.form.domain.*;
import com.regulareport.form.domain.rep.BackSearchRe;
import com.regulareport.form.domain.rep.ReportTaskFillingRep;
import com.regulareport.form.domain.rep.SubCellDefinition;
import com.regulareport.form.domain.req.BackSearch;
import com.regulareport.form.mapper.*;
import com.regulareport.system.mapper.SysDeptMapper;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ReportTaskService {
    private static final Logger log = LoggerFactory.getLogger(ReportTaskService.class);

    private final ReportTaskMapper reportTaskMapper;


    private final SysDeptMapper sysDeptMapper;

    private final HeadSubIndexMapMapper headSubIndexMapMapper;

    private final ReportIndexResultMapper reportIndexResultMapper;

    private final String SUFFIX = "v";

    public ReportTaskService(ReportTaskMapper reportTaskMapper, SysDeptMapper sysDeptMapper, HeadSubIndexMapMapper headSubIndexMapMapper,
                              ReportIndexResultMapper reportIndexResultMapper) {
        this.reportTaskMapper = reportTaskMapper;
        this.sysDeptMapper = sysDeptMapper;
        this.headSubIndexMapMapper = headSubIndexMapMapper;
        this.reportIndexResultMapper = reportIndexResultMapper;
    }

    public List<ReportTaskListRep> list(ReportTaskListReq req) {
        req.setDelFlag(DelFlag.NORMAL.getCode());
        req.setDeptId(null);
        return reportTaskMapper.listReportTask(req);
    }

    public void delete(Detail detail) {
        ReportTask reportTask = new ReportTask();
        reportTask.setTaskId(Long.valueOf(detail.getId()));
        reportTask.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        reportTaskMapper.deleteById(reportTask);
    }

    public ReportTaskRep detail(Detail detail) {
        return reportTaskMapper.selectDetail(Long.valueOf(detail.getId()));
    }

    public List<ReportTaskListRep> findUserTask(LoginUser loginUser) {
        return reportTaskMapper.findUserTask(loginUser);
    }

    public List<ReportTaskListRep> listUser(ReportTaskListReq req) {
        req.setDelFlag(DelFlag.NORMAL.getCode());
        req.setDeptId(SecurityUtils.getLoginUser().getDeptId());
        return reportTaskMapper.listReportTask(req);
    }

    /**
     * 验证该
     * @param filePaths 文件路径列表
     * @return
     */
    private int validateXlsxFiles(List<String> filePaths) {
        int validCount = 0;
        for (String filePath : filePaths) {
            File file = new File(filePath);
            if (file.exists()) {
                validCount++;
            }
        }
        return validCount;
    }

    /**
     * 批量导出
     * @param taskIds
     * @param response
     * @throws IOException
     */
    public void downloadXlsxFilesAsZip(String taskIds, HttpServletResponse response) throws Exception {
        String[] pkIdArr = taskIds.split(",");
        List<ReportTask> reportTaskList = reportTaskMapper.selectReportTaskByTaskIds(pkIdArr);
        List<String> filePaths = reportTaskList.stream().map(ReportTask::getFilePath).collect(Collectors.toList());
        int filePathNum = validateXlsxFiles(filePaths);
        if (filePathNum == 0) {
            throw new IllegalArgumentException("文件路径列表不能为空");
        }

        // 验证文件是否存在且为xlsx文件
        for (String filePath : filePaths) {
            File file = new File(filePath);
            if (!file.exists()) {
                throw new IllegalArgumentException("文件不存在: " + filePath);
            }
            if (!(filePath.toLowerCase().endsWith(".xlsx") || filePath.toLowerCase().endsWith(".xls"))) {
                throw new IllegalArgumentException("文件不是xlsx或xls格式: " + filePath);
            }
        }

        // 设置响应头
        setupResponseHeaders(response, "excel_files.zip");

        try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(response.getOutputStream())) {
            zos.setUseZip64(org.apache.commons.compress.archivers.zip.Zip64Mode.AsNeeded);
            for (String filePath : filePaths) {
                addFileToZip(zos, new File(filePath));
            }
            zos.finish();
         } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 设置HTTP响应头
     */
    private void setupResponseHeaders(HttpServletResponse response, String zipFileName) {
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + zipFileName + "\"");
        response.setCharacterEncoding("UTF-8");
    }

    /**
     * 将单个文件添加到zip压缩包
     */
    private void addFileToZip(ZipArchiveOutputStream zos, File file) throws IOException {
        String fileName = file.getName();
        ZipArchiveEntry entry = new ZipArchiveEntry(file, fileName);
        zos.putArchiveEntry(entry);

        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, length);
            }
            zos.closeArchiveEntry();
        } catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public void adjustCellValue(ReportAdjustCellValueReq req) throws Exception {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        List<ReportIndexResult> reportIndexResults = new ArrayList<>();
        if (req.getTaskId() == null || "".equals(req.getTaskId())) {
            throw new Exception("无单元格调整任务");
        }
        if(req.getAdjustInfos() == null || "".equals(req.getAdjustInfos())){
            throw new Exception("未选择调整单元格");
        }
        List<String> coordinateList = new ArrayList<>();
        Map<String, ReportAdjustInfo> coordinateMap = new HashMap<>();
        for(ReportAdjustInfo info : req.getAdjustInfos()){
            if(Integer.valueOf(info.getCellRow()) != null && Integer.valueOf(info.getCellCol()) != null) {
                CellReference cellRef = new CellReference(info.getCellRow(), info.getCellCol());
                String ref = cellRef.formatAsString();
                coordinateList.add(ref);
                coordinateMap.put(ref, info);
            }
        }

        BackSearch backSearch = new BackSearch();
        backSearch.setDeptId(SecurityUtils.getLoginUser().getDeptId());
        backSearch.getTaskIds().add(Long.valueOf(req.getTaskId()));
        SysDept sysDept = sysDeptMapper.selectDeptById(SecurityUtils.getLoginUser().getDeptId());

        List<BackSearchRe> backSearchRes = reportTaskMapper.findBack(backSearch);
        if (backSearchRes == null || backSearchRes.isEmpty()) {
            throw new Exception("无单元格调整任务");
        }
        Path target = Paths.get(backSearchRes.get(0).getHandleFilePath());
        int version = backSearchRes.get(0).getFileVersion() + 1;
        String name = SUFFIX + version;
        String s = addSuffixToFileName(backSearchRes.get(0).getFileName(), name);
        s = target.getParent().toFile().getAbsolutePath() + "/" + s;
        String dataDate = backSearchRes.get(0).getDataDate();
        Date parse = simpleDateFormat.parse(dataDate);
        backSearchRes.get(0).setDddd(parse);
        backSearchRes.get(0).setDeptNum(sysDept.getDeptNum());

        try (FileInputStream subFis = new FileInputStream(backSearchRes.get(0).getHandleFilePath());
             Workbook subWorkbook = WorkbookFactory.create(subFis);
             FileOutputStream outputStream = new FileOutputStream(s)) {
            subWorkbook.setForceFormulaRecalculation(true);
            Sheet sheetAt = subWorkbook.getSheetAt(0);
            sheetAt.setForceFormulaRecalculation(true);
            for(String coord : coordinateList){
                CellReference ref = new CellReference(coord);
                Cell cell = sheetAt.getRow(ref.getRow()).getCell(ref.getCol());
                String adjustValue = coordinateMap.get(coord).getAdjustValue();
                if(StringUtils.isNotEmpty(adjustValue)){
                    cell.setCellValue(new BigDecimal(adjustValue).doubleValue());
                }
            }

            subWorkbook.write(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
            if ("汇率未配置".equalsIgnoreCase(e.getMessage())) {
                throw e;
            }

            new Exception("补录异常");
        }
        //更新记录表
        ReportTaskFillingRep task = new ReportTaskFillingRep();
        task.setTaskId(backSearchRes.get(0).getTaskId());
        task.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        task.setHandleFilePath(s);
        task.setHandleFileVersion(version);
        task.setJsonState(FileJsonState.U.getCode());
        task.setEditJsonState(FileJsonState.U.getCode());

        List<SubCellDefinition> subCellDefinitions = headSubIndexMapMapper.selectAdjustCellFilling(backSearchRes.get(0).getReportId(), coordinateList);
        try (FileInputStream subFis = new FileInputStream(s);
             Workbook subWorkbook = WorkbookFactory.create(subFis)) {
            Sheet subSheet = subWorkbook.getSheetAt(0);
            FormulaEvaluator evaluator = subWorkbook.getCreationHelper().createFormulaEvaluator();
            for (SubCellDefinition subCellCol : subCellDefinitions) {
                ReportIndexResult reportIndexResult = new ReportIndexResult();
                int cellCol = Integer.parseInt(subCellCol.getCellCol());
                int subRow = Integer.parseInt(subCellCol.getCellRow());
                Cell subCell = subSheet.getRow(subRow).getCell(cellCol);
                BigDecimal cellValueAsString = ExcelToJsonConverter3.getCellValueAsStringOri(subCell, evaluator, true);
                reportIndexResult.setReportFormNo(subCellCol.getReportFormNo());
                reportIndexResult.setReportFormID(backSearchRes.get(0).getReportId());
                reportIndexResult.setIndexNo(subCellCol.getIndexNo());
                reportIndexResult.setIndexName(subCellCol.getIndexName());
                reportIndexResult.setDepartNo(sysDept.getDeptNum());
                reportIndexResult.setDepartName(sysDept.getDeptName());
                reportIndexResult.setIndexSubmittedValue(cellValueAsString);
                reportIndexResult.setReviseDate(new Date(System.currentTimeMillis()));
                reportIndexResult.setDataDate(parse);
                reportIndexResult.setIndexRevisionValue(coordinateMap.get(subCellCol.getCoordinate()).getAdjustValue() != null ? new BigDecimal(coordinateMap.get(subCellCol.getCoordinate()).getAdjustValue()): new BigDecimal("0.00"));
                reportIndexResult.setModifiDescription(coordinateMap.get(subCellCol.getCoordinate()).getReason());
                reportIndexResults.add(reportIndexResult);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("数据打开失败");
        }
        reportTaskMapper.reportAdjust(task);

        List<ReportIndexResult> list = new ArrayList<>();
        for (int i = 0; i < reportIndexResults.size(); i++) {
            list.add(reportIndexResults.get(i));
            if (list.size() >= 500) {
                reportIndexResultMapper.insertBLReportIndexResultBatch(list);
                list.clear();
            }
        }
        if (!list.isEmpty()) {
            reportIndexResultMapper.insertBLReportIndexResultBatch(list);
            list.clear();
        }
    }

    public String addSuffixToFileName(String fileName, String suffix) {
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex != -1) {
            return fileName.substring(0, lastDotIndex) + suffix + fileName.substring(lastDotIndex);
        } else {
            return fileName + suffix;
        }
    }

}
